package com.caregiver.watch.business.user.service.impl;

import com.caregiver.watch.business.user.dao.UserHealthGoalDao;
import com.caregiver.watch.business.user.dao.UserInfoDao;
import com.caregiver.watch.business.user.enums.UserStatusEnum;
import com.caregiver.watch.business.user.helper.InviteCodeHelper;
import com.caregiver.watch.business.user.model.dto.AvatarParam;
import com.caregiver.watch.business.user.model.dto.ChangeEmailParam;
import com.caregiver.watch.business.user.model.dto.ChangePhoneParam;
import com.caregiver.watch.business.user.model.dto.RealNameParam;
import com.caregiver.watch.business.user.model.dto.RegisterParam;
import com.caregiver.watch.business.user.model.dto.UpdateUserHealthGoalParam;
import com.caregiver.watch.business.user.model.dto.UserHealthGoalParam;
import com.caregiver.watch.business.user.model.po.User;
import com.caregiver.watch.business.user.model.po.UserHealthGoal;
import com.caregiver.watch.business.user.model.vo.AccessTokenVo;
import com.caregiver.watch.business.user.model.vo.UserHealthGoalVo;
import com.caregiver.watch.business.user.model.vo.UserInfoVo;
import com.caregiver.watch.business.user.service.AuthService;
import com.caregiver.watch.business.user.service.UserDeviceService;
import com.caregiver.watch.business.user.service.UserService;
import com.caregiver.watch.business.user.utils.IdCardUtils;
import com.caregiver.watch.business.user.utils.SecurityUtils;
import com.caregiver.watch.business.user.utils.UserInfoUtils;
import com.caregiver.watch.business.user.utils.id.IdUtils;
import com.caregiver.watch.business.user.utils.ip.IpUtils;
import com.caregiver.watch.common.enums.SendSmsCodeEnum;
import com.caregiver.watch.common.exception.business.BusinessException;
import com.caregiver.watch.common.exception.system.SystemException;
import com.caregiver.watch.thirdparty.service.DeviceSettingService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import static com.caregiver.watch.common.constant.Constants.APP;
import static com.caregiver.watch.common.constant.NumberConstants.HUNDRED;
import static com.caregiver.watch.common.constant.NumberConstants.ZERO;

/**
 * @Description: 用户服务
 * @Author 疆戟
 * @Date 2025/8/15 0:05
 * @Version 1.0
 */
@AllArgsConstructor
@Service
@Slf4j
public class UserServiceImpl implements UserService {


    private final UserInfoDao userInfoDao;
    private final AuthService authService;
    private final TokenService tokenService;
    private final UserDeviceService userDeviceService;
    private final UserHealthGoalDao userHealthGoalDao;
    private final DeviceSettingService deviceSettingService;

    @Override
    public User queryUserByPhone(String phone) {
        if (StringUtils.isBlank(phone)) {
            throw new BusinessException("手机号不能为空");
        }
        return userInfoDao.selectUserByPhoneNumber(phone);
    }

    @Override
    public User queryUserByUsername(String username) {
        if (StringUtils.isBlank(username)) {
            throw new BusinessException("用户名不能为空");
        }
        return userInfoDao.selectUserByUsername(username);
    }

    @Override
    public Boolean checkPhoneExists(String phone) {
        if (StringUtils.isBlank(phone)) {
            throw new BusinessException("手机号不能为空");
        }
        return userInfoDao.checkPhoneNumberUnique(phone);
    }

    @Override
    public AccessTokenVo register(RegisterParam param) {
        if (StringUtils.isBlank(param.getPhone())) {
            throw new BusinessException("手机号不能为空");
        }
        if (StringUtils.isAnyBlank(param.getPassword(), param.getConfirmPassword())) {
            throw new BusinessException("密码不能为空");
        }
        // 校验密码和确认密码是否相同
        if (!Objects.equals(param.getPassword(), param.getConfirmPassword())) {
            throw new BusinessException("密码和确认密码不一致");
        }
        if (StringUtils.isBlank(param.getCode())) {
            throw new BusinessException("验证码不能为空");
        }
        // 校验验证码
        authService.validateSmsCode(param.getPhone(), param.getCode(), SendSmsCodeEnum.CUSTOMER_REGISTER);
        if (BooleanUtils.isTrue(checkPhoneExists(param.getPhone()))) {
            throw new BusinessException("账号" + param.getPhone() + "已存在");
        }
        try {
            // 构建用户信息
            User user = buildUser(param);
            userInfoDao.insertUser(user);
            // 处理设备信息
            handleDeviceInfo(user, param.getDeviceIds());
            // 处理邀请码信息
            handleInviteCodeInfo(user, param.getInviteCode());
            // 创建token
            return AccessTokenVo.build(tokenService.createToken(user));
        } catch (DuplicateKeyException e) {
            // 这里要捕获唯一索引冲突的异常
            log.error("用户注册异常", e);
            throw new BusinessException("账号/手机号" + param.getPhone() + "已存在");
        } catch (Exception e) {
            log.error("用户注册异常", e);
            throw new SystemException("用户注册异常");
        }
    }


    /**
     * 生成用户名
     */
    private String generateUsername(RegisterParam  param) {
        Set<String> usernames = userInfoDao.queryUsernameByPrefix(param.getPhone());
        if (CollectionUtils.isEmpty(usernames)) {
            return param.getPhone();
        }
        if (usernames.contains(param.getPhone())) {
            // 循环最多100次
            int count = ZERO;
            do {
                String username = param.getPhone() +  IdUtils.generateRandomCode();
                if (!usernames.contains(username)) {
                    return username;
                }
            } while (++count < HUNDRED);
            throw new BusinessException("用户名已存在");
        }
        return param.getPhone();
    }

    /**
     * 处理用户设备信息
     * @param user
     * @param deviceIds
     */
    private void handleDeviceInfo(User user, List<String> deviceIds) {
        if (CollectionUtils.isEmpty(deviceIds)) {
            return;
        }
        userDeviceService.batchInsertUserDevice(user.getUserId(), deviceIds);
    }

    /**
     * 处理邀请码信息
     * @param user
     * @param inviteCode
     */
    private void handleInviteCodeInfo(User user, String inviteCode) {
        if (StringUtils.isEmpty(inviteCode)) {
            return;
        }
    }


    /**
     * 构建用户信息
     *
     * @param param
     * @return
     */
    private User buildUser(RegisterParam param) {
        User user = new User();
        user.setUsername(generateUsername(param));
        user.setNickName(param.getNickName());
        user.setEmail(param.getEmail());
        user.setPhoneNumber(param.getPhone());
        user.setInviteCode(InviteCodeHelper.generateCodeFromPhoneWithSalt(param.getPhone(), APP));
        user.setPassword(SecurityUtils.encryptPassword(param.getPassword()));
        user.setStatus(UserStatusEnum.NORMAL.getCode());
        user.setLoginIp(IpUtils.getIpAddr());
        LocalDateTime now = LocalDateTime.now();
        user.setLoginDate(now);
        user.setCreateBy(param.getPhone());
        user.setUpdateBy(param.getPhone());
        user.setCreateTime(now);
        user.setUpdateTime(now);
        return user;
    }

    @Override
    public Boolean setAvatar(AvatarParam avatar) {
        String avatarUrl = avatar.getAvatarUrl();
        if (StringUtils.isBlank(avatarUrl)) {
            throw new BusinessException("头像地址不能为空");
        }
        Long userId = UserInfoUtils.getUserId();
        boolean exists = userInfoDao.checkUserExists(userId);
        if (!exists) {
            throw new BusinessException("用户不存在");
        }
        // 修改用户 信息
        User user = new User();
        user.setUserId(userId);
        user.setAvatar(avatarUrl);
        return userInfoDao.updateUser(user) > ZERO;
    }

    @Override
    public Boolean realName(RealNameParam param) {
        String realName = param.getRealName();
        if (StringUtils.isEmpty(realName)) {
            throw new BusinessException("真实姓名不能为空");
        }
        String idCard = param.getIdCard();
        if (StringUtils.isEmpty(idCard)) {
            throw new BusinessException("身份证号不能为空");
        }
        if (IdCardUtils.isNoValidIdCard(idCard)) {
            throw new BusinessException("身份证号格式错误");
        }
        Long userId = UserInfoUtils.getUserId();
        boolean exists = userInfoDao.checkUserExists(userId);
        if (!exists) {
            throw new BusinessException("用户不存在");
        }
        // 修改用户 信息
        User user = new User();
        user.setUserId(userId);
        user.setIdCard(idCard);
        user.setRealName(realName);
        return userInfoDao.updateUser(user) > ZERO;
    }

    @Override
    public Boolean changePhone(ChangePhoneParam param) {
        log.info("更换手机号：{}", param);
        if (StringUtils.isBlank(param.getNewPhone())) {
            throw new BusinessException("手机号不能为空");
        }
        if (StringUtils.isBlank(param.getCode())) {
            throw new BusinessException("验证码不能为空");
        }
        // 校验验证码
        authService.validateSmsCode(param.getNewPhone(), param.getCode(), SendSmsCodeEnum.CUSTOMER_PHONE);
        Long userId = UserInfoUtils.getUserId();
        User user = new User();
        user.setUserId(userId);
        user.setPhoneNumber(param.getNewPhone());
        // 校验手机号是否存在
        if (userInfoDao.checkPhoneNumberUnique(param.getNewPhone())) {
            throw new BusinessException("手机号" + param.getNewPhone() + "已存在");
        }
        try {
            return userInfoDao.updateUser(user) > ZERO;
        } catch (DuplicateKeyException e) {
            // 这里要捕获唯一索引冲突的异常
            log.error("修改手机号异常", e);
            throw new BusinessException("手机号" + param.getNewPhone() + "已存在");
        } catch (Exception e) {
            log.error("修改手机号异常", e);
            throw new SystemException("修改手机号异常");
        }
    }


    @Override
    public Boolean changeEmail(ChangeEmailParam param) {
        log.info("更换邮箱：{}", param.getNewEmail());
        if (StringUtils.isBlank(param.getNewEmail())) {
            throw new BusinessException("邮箱不能为空");
        }
        Long userId = UserInfoUtils.getUserId();
        User user = new User();
        user.setUserId(userId);
        user.setEmail(param.getNewEmail());
        return userInfoDao.updateUser(user) > ZERO;
    }

    @Override
    public UserInfoVo getUserInfo() {
        Long userId = UserInfoUtils.getUserId();
        User user = userInfoDao.selectUserById(userId);
        // 用户状态正常
        if (Objects.equals(user.getStatus(), UserStatusEnum.NORMAL.getCode())) {
            return UserInfoVo.build(user);
        }
        throw new BusinessException("用户状态异常");
    }

    @Override
    public Boolean setUserHealthGoal(UserHealthGoalParam param) {
        Long userId = UserInfoUtils.getUserId();
        UserHealthGoal po = param.toPo(userId);
        Boolean success;
        // 查询是否已经存在配置
        Long id = userHealthGoalDao.queryIdByUserId(userId);
        if (Objects.nonNull(id)) {
            po.setId(id);
            success = userHealthGoalDao.updateById(po) > ZERO;
        } else {
            try {
                success = userHealthGoalDao.insert(po) > ZERO;
            } catch (DuplicateKeyException e) {
                // 这里要捕获唯一索引冲突的异常
                log.error("修改用户健康目标异常", e);
                Long newId = userHealthGoalDao.queryIdByUserId(userId);
                if (Objects.nonNull(newId)) {
                    po.setId(newId);
                    return userHealthGoalDao.updateById(po) > ZERO;
                }
                return Boolean.FALSE;
            }
        }
        // 保存成功后需要通知手表
        if (BooleanUtils.isTrue(success)) {

        }
        return success;
    }

    @Override
    public UserHealthGoalVo queryUserHealthGoal() {
        Long userId = UserInfoUtils.getUserId();
        return queryUserHealthGoal(userId);
    }

    @Override
    public UserHealthGoalVo queryUserHealthGoal(Long userId) {
        UserHealthGoal userHealthGoal = userHealthGoalDao.selectByUserId(userId);
        return UserHealthGoalVo.build(userHealthGoal);
    }
}
